package cn.seaboot.admin.mvc.valid;

import cn.seaboot.admin.mvc.argument.ModifyArgument;
import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.commons.core.Converter;
import cn.seaboot.commons.reflect.FieldAccess;
import cn.seaboot.commons.reflect.ObjectField;

import javax.validation.*;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * Java 对象校验工具
 *
 * @author Mr.css
 * @version 2023-09-21 11:12
 */
public class ValidationUtils {
    private static final ValidatorFactory validatorFactory;
    private static final Validator validator;

    static {
        validatorFactory = Validation.buildDefaultValidatorFactory();
        validator = validatorFactory.getValidator();
    }

    /**
     * 销毁对象实体
     */
    public static void destroy() {
        validatorFactory.close();
    }

    /**
     * javax.validation 验证对象是否有效
     * <p>
     * 如果校验失败，则抛出异常，spring 环境下可以写一个统一的异常捕获切面
     *
     * @param obj    校验实体
     * @param groups 校验分组
     * @throws ConstraintViolationException 抛出验证失败异常
     */
    public static void valid(Object obj, Class<?>... groups) {
        Set<ConstraintViolation<Object>> set = validator.validate(obj, groups);
        if (CommonUtils.isNotEmpty(set)) {
            throw new ConstraintViolationException(set);
        }
    }

    /**
     * javax.validation 验证对象是否有效
     * <p>
     * 如果校验失败，则抛出异常，spring 环境下可以写一个统一的异常捕获切面
     *
     * @param arg    快捷变更字段
     * @param clazz  字段归属的对象
     * @param groups 校验分组
     * @throws ConstraintViolationException 抛出验证失败异常
     */
    public static void validArgument(ModifyArgument arg, Class<?> clazz, Class<?>... groups) {
        FieldAccess access = FieldAccess.create(clazz);
        ObjectField field = access.getField(arg.getField());

        if(field == null) {
            throw new IllegalArgumentException("field not found:" + arg.getField());
        }

        // try case to target type
        Object val = Converter.convert(arg.getValue(), field.getType());

        // if succeeded, generate the instance.
        Object ret = access.newInstance();
        access.setValue(ret, arg.getField(), val);

        // check single field
        Set<ConstraintViolation<Object>> set = validator.validateProperty(ret, arg.getField(), groups);
        if (CommonUtils.isNotEmpty(set)) {
            throw new ConstraintViolationException(set);
        }
    }

    /**
     * javax.validation 验证方法入参是否有效
     * <p>
     * 如果校验失败，则抛出异常，spring 环境下可以写一个统一的异常捕获切面
     *
     * @param obj    校验实体
     * @param groups 校验分组
     * @throws ConstraintViolationException 抛出验证失败异常
     */
    public static void validParameters(Object obj, Method method, Object[] args, Class<?>... groups) {
        Set<ConstraintViolation<Object>> set = validator.forExecutables().validateParameters(obj, method, args, groups);
        if (CommonUtils.isNotEmpty(set)) {
            throw new ConstraintViolationException(set);
        }
    }
}
